package fem2.gui.pm;

import fem2.gui.ModelHolder;
import fem2.gui.ModelObserver;
import fem2.gui.src.HofemSource;
import inf.jlinalg.Vector2D;
import inf.vtk.pm.AbstractDataProvider;
import inf.vtk.pm.Pipeline;

import java.beans.PropertyChangeEvent;
import java.util.List;

/**
 * Base class for hofem model based data providers.
 * 
 * @author Matthias Baitsch
 */
public abstract class HofemDataProvider extends AbstractDataProvider implements
		ModelObserver {

	/** Solution data property */
	private static final String PROP_SOLUTION = "Solution";
	/** Corresponding model type */
	public static Class<ModelHolder> MODEL_TYPE = ModelHolder.class;

	protected HofemDataProvider() {
	}

	/**
	 * Returns the model instance as miniFE model.
	 * 
	 * @return the model. This method never returns a <code>null</code>
	 *         reference.
	 */
	protected final ModelHolder getCastedModel() {
		return (ModelHolder) getModel();
	}

	/**
	 * Returns the bounds of the miniFE model.
	 * 
	 * @return the bounds in an array [xmin, xmax, ymin, ymax, zmin, zmax]
	 */
	@Override
	public double[] getModelBounds() {
		ModelHolder model = getCastedModel();
		int nn = model.getModel().getMesh().countNodes();
		double[] bounds = { Double.MAX_VALUE, -Double.MAX_VALUE,
				Double.MAX_VALUE, -Double.MAX_VALUE, Double.MAX_VALUE,
				-Double.MAX_VALUE };

		for (int i = 0; i < nn; i++) {
			Vector2D p = new Vector2D(model.getModel().getMesh().getNode(i)
					.getPosition());
			double x1 = p.getX1();
			double x2 = p.getX2();
			double x3 = 0;

			if (x1 < bounds[0])
				bounds[0] = x1;
			if (x1 > bounds[1])
				bounds[1] = x1;
			if (x2 < bounds[2])
				bounds[2] = x2;
			if (x2 > bounds[3])
				bounds[3] = x2;
			if (x3 < bounds[4])
				bounds[4] = x3;
			if (x3 > bounds[5])
				bounds[5] = x3;
		}
		return bounds;
	}

	/**
	 * Returns the sources for this data provider.
	 * 
	 * @return a list of source objects
	 */
	protected abstract List<HofemSource> getSources();

	/**
	 * Invoked when the model has changed.
	 * 
	 * @param evt
	 *            the change event.
	 */
	protected abstract void modelChange(PropertyChangeEvent evt);

	@Override
	public void releaseModel() {
	}

	@Override
	public final void setModel(Object model) {
		ModelHolder newModel = (ModelHolder) model;
		ModelHolder oldModel = getCastedModel();

		if (model == null) {
			throw new NullPointerException();
		}
		if (!(model instanceof ModelHolder)) {
			throw new IllegalArgumentException();
		}

		if (oldModel != null) {
			oldModel.removeObserver(this);
		}
		newModel.addObserver(this);

		for (HofemSource src : getSources()) {
			src.setModel(newModel);
		}
		super.setModel(model);
		modelChange(new PropertyChangeEvent(this, Pipeline.PROP_MODEL, null,
				null));
	}

	@Override
	public void solutionChanged(ModelHolder m) {
		solutionChanged(new PropertyChangeEvent(this, PROP_SOLUTION, null, null));
		firePropertyChange(PROP_SOLUTION, null);
	}

	protected abstract void solutionChanged(PropertyChangeEvent p);
}
